import hashlib
import json
import logging
import os
import sys
import time
import urllib
from datetime import datetime
from logging.handlers import RotatingFileHandler
from typing import Dict, List, Any

import pymysql
import requests
from flask import Blueprint, request, jsonify
from pymysql.cursors import DictCursor

# 添加项目根目录到路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from config import DB

# 创建蓝图
allocate_bp = Blueprint('allocate', __name__)

# 配置调拨单专用日志
allocate_logger = logging.getLogger('allocate_operations')
allocate_logger.setLevel(logging.INFO)

# 创建日志目录
log_dir = os.path.dirname(os.path.abspath(__file__))
log_file_path = os.path.join(log_dir, 'allocate_operations.log')

# 创建文件处理器
allocate_handler = RotatingFileHandler(
    log_file_path,
    maxBytes=5000000,  # 5MB
    backupCount=3,
    encoding='utf-8'
)
allocate_handler.setFormatter(logging.Formatter(
    '%(asctime)s - %(levelname)s - %(message)s'
))
allocate_logger.addHandler(allocate_handler)

# 通用日志
logger = logging.getLogger(__name__)


class JiKeYunConfig:
    """吉客云API配置类"""

    def __init__(self):
        self.app_key = '12540443'
        self.app_secret = 'a7860451c1b6470da900bd40d91addb2'
        self.api_url = "https://open.jackyun.com/open/openapi/do"
        self.version = '1.0'
        self.content_type = 'json'

        # 默认业务配置
        self.default_apply_user_id = "2167569116603155969"
        self.default_apply_user_name = "数据中心-小烟"
        self.default_depart_code = "0174"  # 采购部
        self.default_company_code = "00002-01"  # 最西域
        self.default_currency_code = "CNY"
        self.default_currency_name = "人民币"
        self.default_currency_rate = "1"


class DatabaseConfig:
    """数据库配置类"""

    def __init__(self):
        self.host = "rm-2zer2f33rws29ogb1do.mysql.rds.aliyuncs.com"
        self.port = 3306
        self.user = "zxy"
        self.password = "zxy@800901"
        self.database = "ruoyi"
        self.charset = 'utf8mb4'


class JiKeYunAPIClient:
    """吉客云API客户端类"""

    def __init__(self, config: JiKeYunConfig = None):
        self.config = config or JiKeYunConfig()

    def _generate_signature(self, params: Dict[str, Any]) -> str:
        """生成API签名"""
        sorted_params = sorted(params.items())
        query_string = ""
        for key, value in sorted_params:
            if key and value:
                query_string += f"{key}{value}"

        sign_string = (self.config.app_secret + query_string + self.config.app_secret).lower()
        return hashlib.md5(sign_string.encode('utf-8')).hexdigest().lower()

    def _prepare_request_data(self, method: str, bizdata: Dict[str, Any]) -> str:
        """准备请求数据"""
        bizcontent_str = json.dumps(bizdata, ensure_ascii=False)

        params_for_sign = {
            'method': method,
            'appkey': self.config.app_key,
            'version': self.config.version,
            'contenttype': self.config.content_type,
            'timestamp': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
            'bizcontent': bizcontent_str
        }

        # 生成签名
        signature = self._generate_signature(params_for_sign)
        params_for_sign['sign'] = signature

        # 构造请求体
        post_data_parts = []
        for key, value in params_for_sign.items():
            if key == 'bizcontent':
                post_data_parts.append(f"{key}={urllib.parse.quote(str(value), safe='')}")
            else:
                post_data_parts.append(f"{key}={str(value)}")

        return "&".join(post_data_parts)

    def send_request(self, method: str, bizdata: Dict[str, Any]) -> Dict[str, Any]:
        """发送API请求"""
        try:
            post_data_str = self._prepare_request_data(method, bizdata)

            headers = {
                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
            }

            response = requests.post(
                self.config.api_url,
                data=post_data_str.encode('utf-8'),
                headers=headers,
                timeout=30
            )
            response.raise_for_status()

            api_response = response.json()
            logger.info(f"吉客云API响应: {api_response}")
            return api_response

        except requests.exceptions.Timeout:
            logger.error("API请求超时")
            return self._create_error_response('TIMEOUT', '请求超时，请稍后重试')
        except requests.exceptions.ConnectionError:
            logger.error("网络连接错误")
            return self._create_error_response('CONNECTION_ERROR', '网络连接失败，请检查网络设置')
        except requests.exceptions.HTTPError as e:
            logger.error(f"HTTP错误: {e}")
            return self._create_error_response(f'HTTP_{e.response.status_code}', f'HTTP错误: {e.response.status_code}')
        except json.JSONDecodeError:
            logger.error("响应不是有效的JSON格式")
            return self._create_error_response('INVALID_JSON', '服务器响应格式错误')
        except requests.exceptions.RequestException as e:
            logger.error(f"请求异常: {e}")
            return self._create_error_response('REQUEST_ERROR', f'请求失败: {str(e)}')
        except Exception as e:
            logger.error(f"未知错误: {e}", exc_info=True)
            return self._create_error_response('UNKNOWN_ERROR', f'未知错误: {str(e)}')

    def _create_error_response(self, code: str, message: str) -> Dict[str, Any]:
        """创建错误响应"""
        return {
            'success': False,
            'code': code,
            'message': message
        }


class AllocateOrderService:
    """调拨单业务服务类"""

    def __init__(self, api_client: JiKeYunAPIClient = None):
        self.api_client = api_client or JiKeYunAPIClient()
        self.config = self.api_client.config

    def create_allocate_order(self, order_data: Dict[str, Any]) -> Dict[str, Any]:
        """创建调拨单"""
        try:
            # 构造业务数据
            bizdata = self._build_allocate_bizdata(order_data)

            # 发送API请求
            response = self.api_client.send_request('erp.allocate.create', bizdata)

            # 处理响应
            return self._process_response(response)

        except Exception as e:
            logger.error(f"创建调拨单失败: {e}", exc_info=True)
            return {
                'success': False,
                'code': 'CREATE_ERROR',
                'message': f'创建调拨单失败: {str(e)}'
            }

    def _build_allocate_bizdata(self, order_data: Dict[str, Any]) -> Dict[str, Any]:
        """构造调拨单业务数据"""
        current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        current_date = time.strftime("%Y-%m-%d", time.localtime())

        # 处理调拨明细数据
        raw_stock_details = order_data.get('stockAllocateDetailViews', [])
        stock_details = self._process_stock_details(raw_stock_details)

        # 计算总数量
        total_count = sum(item.get('skuCount', 0) if isinstance(item, dict) else 0 for item in stock_details)

        bizdata = {
            "allocateType": "0",
            "applyUserId": order_data.get('applyUserId', self.config.default_apply_user_id),
            "applyUserName": order_data.get('applyUserName', self.config.default_apply_user_name),
            "applyDate": order_data.get('applyDate', current_date),  # 外部不传则自动获取当前日期
            "departCode": order_data.get('departCode', self.config.default_depart_code),
            "companyCode": order_data.get('companyCode', self.config.default_company_code),
            "currencyRate": order_data.get('currencyRate', self.config.default_currency_rate),
            "outCurrencyCode": order_data.get('outCurrencyCode', self.config.default_currency_code),
            "outCurrencyName": order_data.get('outCurrencyName', self.config.default_currency_name),
            "inCurrencyCode": order_data.get('inCurrencyCode', self.config.default_currency_code),
            "inCountSum": order_data.get('inCountSum', total_count),
            "outCountSum": order_data.get('outCountSum', total_count),
            "outWarehouseCode": order_data['outWarehouseCode'],  # 必填
            "intWarehouseCode": order_data['intWarehouseCode'],  # 必填
            "status": order_data.get('status', 0),
            "goodsCount": order_data.get('goodsCount', len(stock_details)),
            "skuCount": order_data.get('skuCount', total_count),
            "stockAllocateDetailViews": stock_details
        }

        # 可选字段
        if 'totalAmount' in order_data:
            bizdata['totalAmount'] = order_data['totalAmount']

        # gmtCreate字段：外部不传则自动获取当前时间
        bizdata['gmtCreate'] = order_data.get('gmtCreate', current_time)

        return bizdata

    def _process_stock_details(self, raw_stock_details: List[Any]) -> List[Dict[str, Any]]:
        """处理调拨明细数据，将数组格式转换为标准格式"""
        processed_details = []

        for item in raw_stock_details:
            if isinstance(item, list) and len(item) >= 8:
                # 数组格式: [id, barcode, name, code, price, count, unit_name, remark]
                detail = {
                    "skuBarcode": item[1],  # 商品条码
                    "skuCount": int(item[5]) if str(item[5]).isdigit() else 1,  # 数量
                    "unitName": item[6],  # 单位名称
                    "rowRemark": item[7],  # 备注
                    "isCertified": 1,  # 默认已认证
                }

                # 添加价格信息（第4位是价格）
                if len(item) > 4 and item[4]:
                    try:
                        detail["skuPrice"] = float(item[4])
                    except (ValueError, TypeError):
                        pass

                processed_details.append(detail)
            else:
                logger.warning(f"无法处理的调拨明细数据格式: {item}")

        return processed_details

    def _process_response(self, response: Dict[str, Any]) -> Dict[str, Any]:
        """处理API响应"""
        if response.get('code') == 200 and response.get('result', {}).get('data') is not None:
            # 直接返回调拨单号，不用data包裹
            result_data = response.get('result', {}).get('data')
            # 如果result_data是字典且包含allocateNo，则提取allocateNo
            if isinstance(result_data, dict) and 'allocateNo' in result_data:
                allocate_no = result_data['allocateNo']
            else:
                # 否则直接使用result_data
                allocate_no = result_data

            return {
                'success': True,
                'message': '调拨单创建成功',
                'data': allocate_no
            }
        else:
            error_msg = response.get('msg', response.get('message', '未知错误'))
            sub_code = response.get('subCode', response.get('code', ''))
            return {
                'success': False,
                'code': sub_code,
                'message': f'调拨单创建失败: {error_msg}'
            }


class AllocateOrderSaveService:
    """调拨单保存服务类"""

    def __init__(self, db_config: DatabaseConfig = None):
        self.db_config = db_config or DatabaseConfig()

    def get_connection(self):
        """获取数据库连接"""
        try:
            # 使用config.py中的DB函数获取连接，然后重新配置
            connection = DB()
            connection.close()  # 关闭原连接

            # 重新创建连接，添加需要的配置
            connection = pymysql.connect(
                host=self.db_config.host,
                port=self.db_config.port,
                user=self.db_config.user,
                password=self.db_config.password,
                database=self.db_config.database,
                charset=self.db_config.charset,
                cursorclass=DictCursor,
                autocommit=False
            )
            return connection
        except Exception as e:
            logger.error(f"数据库连接失败: {str(e)}")
            raise

    def save_allocate_order(self, order_data: Dict[str, Any]) -> Dict[str, Any]:
        """保存调拨单信息"""
        connection = None
        try:
            connection = self.get_connection()

            # 开始事务
            connection.begin()

            # 保存主表信息
            main_result = self._save_main_order(connection, order_data)
            if not main_result['success']:
                connection.rollback()
                return main_result

            # 提交事务
            connection.commit()

            logger.info(f"调拨单保存成功: {order_data.get('allocate_no')}")
            return {
                'success': True,
                'message': '调拨单保存成功',
                'data': order_data.get('allocate_no')
            }

        except Exception as e:
            if connection:
                connection.rollback()
            logger.error(f"保存调拨单失败: {str(e)}")
            return {
                'success': False,
                'message': f'保存调拨单失败: {str(e)}'
            }
        finally:
            if connection:
                connection.close()

    def _save_main_order(self, connection, order_data: Dict[str, Any]) -> Dict[str, Any]:
        """保存调拨单主表信息"""
        try:
            cursor = connection.cursor()

            # 检查调拨单是否已存在
            check_sql = "SELECT id FROM allocate_orders WHERE allocate_no = %s"
            cursor.execute(check_sql, (order_data.get('allocate_no'),))
            existing = cursor.fetchone()

            if existing:
                return {
                    'success': False,
                    'message': f"调拨单号 {order_data.get('allocate_no')} 已存在"
                }

            # 插入主表数据
            insert_sql = """
                INSERT INTO allocate_orders (
                    allocate_no, out_warehouse_id, out_warehouse_code, out_warehouse_name,
                    int_warehouse_id, int_warehouse_code, int_warehouse_name,
                    apply_date, out_count_sum, total_amount, stock_details,
                    remark
                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
            """

            # 解析申请日期
            apply_date = None
            if order_data.get('apply_date'):
                try:
                    apply_date = datetime.strptime(order_data['apply_date'], '%Y-%m-%d %H:%M:%S')
                except:
                    apply_date = datetime.now()

            # 将货品明细转换为JSON字符串
            stock_details_json = None
            if order_data.get('stock_details'):
                stock_details_json = json.dumps(order_data['stock_details'], ensure_ascii=False)

            cursor.execute(insert_sql, (
                order_data.get('allocate_no'),
                order_data.get('out_warehouse_id', ''),
                order_data.get('out_warehouse_code'),
                order_data.get('out_warehouse_name', ''),
                order_data.get('int_warehouse_id', ''),
                order_data.get('int_warehouse_code'),
                order_data.get('int_warehouse_name', ''),
                apply_date,
                order_data.get('out_count_sum', 0),
                order_data.get('total_amount', 0),
                stock_details_json,
                order_data.get('remark', '')
            ))

            return {'success': True}

        except Exception as e:
            logger.error(f"保存主表信息失败: {str(e)}")
            return {
                'success': False,
                'message': f'保存主表信息失败: {str(e)}'
            }

    def get_allocate_order(self, allocate_no: str) -> Dict[str, Any]:
        """根据调拨单号查询调拨单信息"""
        connection = None
        try:
            connection = self.get_connection()
            cursor = connection.cursor()

            # 查询主表信息
            main_sql = "SELECT * FROM allocate_orders WHERE allocate_no = %s"
            cursor.execute(main_sql, (allocate_no,))
            main_info = cursor.fetchone()

            if not main_info:
                return {
                    'success': False,
                    'message': '调拨单不存在'
                }

            # 解析JSON格式的货品明细
            if main_info.get('stock_details'):
                try:
                    main_info['stock_details'] = json.loads(main_info['stock_details'])
                except:
                    main_info['stock_details'] = []

            return {
                'success': True,
                'data': main_info
            }

        except Exception as e:
            logger.error(f"查询调拨单失败: {str(e)}")
            return {
                'success': False,
                'message': f'查询调拨单失败: {str(e)}'
            }
        finally:
            if connection:
                connection.close()


@allocate_bp.route('/allocate/create', methods=['POST'])
def create_allocate_order():
    """
    创建调拨单接口
    """
    try:
        # 检查请求内容类型
        if not request.is_json:
            logger.error(f'请求内容类型错误: {request.content_type}')
            return jsonify({
                'success': False,
                'message': '请求必须是JSON格式'
            }), 400

        # 获取原始数据用于调试
        raw_data = request.get_data(as_text=True)
        # logger.info(f'接收到的原始数据: {raw_data}')

        # 获取请求数据
        try:
            request_data = request.get_json()
        except Exception as json_error:
            logger.error(f'JSON解析失败: {str(json_error)}, 原始数据: {raw_data}')
            return jsonify({
                'success': False,
                'message': f'JSON格式错误: {str(json_error)}'
            }), 400

        # 检查请求数据是否为空
        if not request_data:
            logger.error('请求数据为空')
            return jsonify({
                'success': False,
                'message': '请求数据不能为空'
            }), 400

        logger.info(f'创建调拨单请求数据: {request_data}')

        # 验证必填字段
        required_fields = ['outWarehouseCode', 'intWarehouseCode', 'stockAllocateDetailViews']
        missing_fields = []
        for field in required_fields:
            value = request_data.get(field)
            if not value or (isinstance(value, str) and not value.strip()):
                missing_fields.append(field)

        if missing_fields:
            error_msg = f'缺少必填字段: {", ".join(missing_fields)}'
            logger.error(error_msg)
            return jsonify({
                'success': False,
                'message': error_msg
            }), 400

        # 验证调拨明细数据
        stock_details = request_data.get('stockAllocateDetailViews', [])
        if not stock_details:
            error_msg = '调拨明细数据不能为空'
            logger.error(error_msg)
            return jsonify({
                'success': False,
                'message': error_msg
            }), 400

        # 调用创建调拨单函数
        service = AllocateOrderService()
        result = service.create_allocate_order(request_data)

        # 记录操作结果到专用日志
        out_warehouse = request_data.get('outWarehouseCode', '未知仓库')
        in_warehouse = request_data.get('intWarehouseCode', '未知仓库')
        detail_count = len(stock_details)

        if result['success']:
            allocate_logger.info(
                f'调拨单创建成功 - 调出仓库: {out_warehouse}, 调入仓库: {in_warehouse}, 明细数量: {detail_count}')
            return jsonify(result), 200
        else:
            allocate_logger.error(
                f'调拨单创建失败 - 调出仓库: {out_warehouse}, 调入仓库: {in_warehouse}, 错误信息: {result.get("message", "未知错误")}')
            return jsonify(result), 500

    except Exception as e:
        logger.error(f'创建调拨单接口处理过程中发生错误: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': f'服务器错误: {str(e)}'
        }), 500


@allocate_bp.route('/allocate/save', methods=['POST'])
def save_allocate_order():
    """保存调拨单接口"""
    try:
        # 获取请求数据
        request_data = request.get_json()
        if not request_data:
            return jsonify({
                'success': False,
                'message': '请求数据不能为空'
            }), 400

        # 验证必填字段
        required_fields = ['allocate_no', 'out_warehouse_code', 'int_warehouse_code']
        for field in required_fields:
            if not request_data.get(field):
                return jsonify({
                    'success': False,
                    'message': f'缺少必填字段: {field}'
                }), 400

        # 创建服务实例并保存
        service = AllocateOrderSaveService()
        result = service.save_allocate_order(request_data)

        status_code = 200 if result['success'] else 400
        return jsonify(result), status_code

    except Exception as e:
        logger.error(f"保存调拨单接口异常: {str(e)}")
        return jsonify({
            'success': False,
            'message': f'服务器内部错误: {str(e)}'
        }), 500


@allocate_bp.route('/allocate/get/<allocate_no>', methods=['GET'])
def get_allocate_order(allocate_no: str):
    """查询调拨单接口"""
    try:
        service = AllocateOrderSaveService()
        result = service.get_allocate_order(allocate_no)

        status_code = 200 if result['success'] else 404
        return jsonify(result), status_code

    except Exception as e:
        logger.error(f"查询调拨单接口异常: {str(e)}")
        return jsonify({
            'success': False,
            'message': f'服务器内部错误: {str(e)}'
        }), 500


if __name__ == "__main__":
    # 测试代码
    service = AllocateOrderService()
    # 数组格式测试数据（与实际接收数据格式一致）
    order_data_array = {
        "outWarehouseCode": "0001",  # 调出仓库编码（必填）
        "intWarehouseCode": "0337",  # 调入仓库编码（必填）
        "stockAllocateDetailViews": [
            # 数组格式: [id, barcode, name, code, unit, count, unit_name, remark]
            ["2300207089796908672", "6975932684397", "【TC-酒水/饮料】伊昭新疆奶皮子中式精酿啤酒1L", "tc_yzxjnpzpijiu1L",
             "10", 2, "罐", "测试"]
        ],
        "applyDate": "2025-09-16 11:47:55"
    }

    print("测试数组格式数据:")
    result = service.create_allocate_order(order_data_array)
    print(json.dumps(result, indent=2, ensure_ascii=False))
